/*
 * Copyright (c) 2020-2021. the original authors and DEPSEA.ORG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.depsea.oss.client;

import jakarta.servlet.http.HttpServletResponse;
import org.depsea.oss.exception.OssDownloadException;
import org.depsea.oss.exception.OssException;
import org.depsea.oss.exception.OssGetFileException;
import org.depsea.oss.exception.OssUploadException;
import org.depsea.oss.filter.FileUploadFilter;
import org.depsea.oss.model.FileContext;
import org.depsea.oss.model.FileInfo;
import org.depsea.oss.model.FileStreamInfo;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 通用
 *
 * @author Wang Chengwei
 * @since 1.0.0 2021/12/1 17:54
 */
public abstract class AbstractOssTemplate {

    protected FileInfo doUpload(String bucketName, String folder, String filename, String originalFilename, String contentType, InputStream ins) {
        try {
            long size = ins.available();
            FileContext fileContext = new FileContext(originalFilename, StringUtils.getFilenameExtension(originalFilename), size);
            for (FileUploadFilter filter : this.getFilters()) {
                filter.doFilter(fileContext);
            }
            return this.upload(size, bucketName, folder, filename, originalFilename, contentType, ins);
        } catch (OssException e) {
            throw e;
        } catch (Exception e) {
            throw new OssUploadException("[" + this.getOssTypeName() + "] 文件上传失败", e);
        }
    }

    protected FileInfo upload(long size, String bucketName, String folder, String filename, String originalFilename, String contentType, InputStream ins)
            throws Exception {

        String fullPath = this.getFullPath(folder, filename);

        FileInfo fileInfo = new FileInfo();
        fileInfo.setFilename(filename);
        fileInfo.setOriginalFilename(originalFilename);
        fileInfo.setFullPath(fullPath);
        fileInfo.setContentType(contentType);
        fileInfo.setFileExtension(StringUtils.getFilenameExtension(originalFilename));
        fileInfo.setSize(size);

        this.doUpload(size, bucketName, fullPath, originalFilename, contentType, ins);

        return fileInfo;
    }

    protected String getFullPath(String folder, String filename) {
        if (!folder.endsWith("/")) {
            folder = folder + "/";
        }
        return folder + filename;
    }

    public FileStreamInfo doGet(String bucketName, String fullPath) {
        try {
            FileStreamInfo streamInfo = this.doGetOssObject(bucketName, fullPath);
            if (streamInfo.getFullPath().contains("/")) {
                streamInfo.setFilename(streamInfo.getFullPath().substring(streamInfo.getFullPath().indexOf("/")));
            } else {
                streamInfo.setFilename(streamInfo.getFullPath());
            }
            return streamInfo;
        } catch (Exception e) {
            throw new OssGetFileException(e.getMessage(), e);
        }
    }

    public void doDownload(String bucketName, String fullPath, HttpServletResponse response) {
        try {
            FileStreamInfo streamInfo = this.doGet(bucketName, fullPath);
            this.doDownload(streamInfo.getOriginalFilename(), streamInfo.getFileData(), streamInfo.getContentType(), response);
        } catch (OssException e) {
            throw e;
        } catch (IOException e) {
            throw new OssDownloadException("[" + this.getOssTypeName() + "] 文件[" + fullPath + "]下载失败");
        }
    }

    public void doDownload(String bucketName, String fullPath, String filename, HttpServletResponse response) {
        try {
            FileStreamInfo streamInfo = this.doGet(bucketName, fullPath);
            this.doDownload(filename, streamInfo.getFileData(), streamInfo.getContentType(), response);
        } catch (OssException e) {
            throw e;
        } catch (IOException e) {
            throw new OssDownloadException("[" + this.getOssTypeName() + "] 文件[" + fullPath + "]下载失败");
        }
    }

    public void doPreview(String bucketName, String fullPath, HttpServletResponse response) {
        try {
            FileStreamInfo streamInfo = this.doGet(bucketName, fullPath);
            response.setContentType(streamInfo.getContentType());
            response.setContentLengthLong(streamInfo.getSize());
            response.getOutputStream().write(streamInfo.getFileData());
            response.getOutputStream().flush();
        } catch (OssException e) {
            throw e;
        } catch (IOException e) {
            throw new OssDownloadException("[" + this.getOssTypeName() + "] 文件[" + fullPath + "]预览失败");
        }
    }

    protected void doDownload(String filename, byte[] fileData, String contentType, HttpServletResponse response) throws IOException {
        response.setContentType(contentType);
        response.setContentLengthLong(fileData.length);

        String disposition = "attachment;fileName=\"" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()) + "\"";
        response.setHeader("Content-Disposition", disposition);
        response.getOutputStream().write(fileData);
        response.getOutputStream().flush();

    }

    /**
     * 获取过滤器
     */
    protected abstract List<FileUploadFilter> getFilters();

    /**
     * 获取Oss类型名称
     */
    protected abstract String getOssTypeName();

    /**
     * 上传文件
     *
     * @param size 文件大小
     * @param bucketName Bucket Name
     * @param fullPath 完整的文件路径
     * @param originalFilename 原始文件名
     * @param contentType 文件类型
     * @param ins 数据流
     * @throws Exception 上传失败抛出异常
     */
    protected abstract void doUpload(long size, String bucketName, String fullPath, String originalFilename, String contentType, InputStream ins) throws Exception;


    /**
     * 获取文件信息
     *
     * @param bucketName Bucket Name
     * @param fullPath 完整的文件路径
     * @return 文件信息及文件流
     * @throws Exception 获取失败
     */
    protected abstract FileStreamInfo doGetOssObject(String bucketName, String fullPath) throws Exception;


}
